/**
cl-args.c - Created by Timothy Morey on 3/6/2012.

This file contains the implementation of common functions used to parse 
command line arguments.
*/

#include <stdio.h>
#include <string.h>

#include "cl-args.h"
#include "err.h"
#include "logger.h"

#ifdef __WINDOWS__
#pragma warning(disable:4996)
#endif


/******************************************************************************
* Forward declarations for local functions:
******************************************************************************/

const ArgDef* GetArgDef(const char* name, CLSchema* schema);
void PrintArgOverview(ArgDef* arg);


/******************************************************************************
* Implementations for functions defined in cl-args.h:
******************************************************************************/

int ContainsArg(int argc, char* argv[], const char* arg, CLSchema* schema, int* value)
{
  int retval = NVN_NOERR;

  if(value)
  {
    int i = 0;              /* Loop counter */
    const ArgDef* def = 0;  /* Temporary ArgDef pointer */

    *value = 0;
    for(i = 1; !retval && i < argc; i++)
    {
      if(strlen(argv[i]) > 1 && (argv[i])[0] == '-')
      {
        def = GetArgDef(&((argv[i])[1]), schema);
        if(def && 0 == strcmp(def->Name, arg))
          *value = 1;
        else if(def && def->RequiresValue)
          i++;
      }
    }
  }
  else
  {
    retval = NVN_EINVARGS;
  }

  return retval;
}

int GetArgValue(int argc, char* argv[], const char* arg, CLSchema* schema, const char** value)
{
  int retval = NVN_NOERR;

  if(value)
  {
    int i = 0;               /* Loop counter */
    const ArgDef* def = 0;   /* Temporary ArgDef pointer */

    *value = 0;
    for(i = 1; !retval && i < argc; i++)
    {
      if(strlen(argv[i]) > 1 && (argv[i])[0] == '-')
      {
        def = GetArgDef(&((argv[i])[1]), schema);
        if(def && def->RequiresValue && 0 == strcmp(def->Name, arg) && i+1 < argc)
          *value = argv[i+1];
        else if(def && def->RequiresValue)
          i++;
      }
    }
  }
  else
  {
    retval = NVN_EINVARGS;
  }

  return retval;
}

int PrintArgSchema(int argc, char* argv[], CLSchema* schema)
{
  int retval = NVN_NOERR;

  if(schema)
  {
    int i = 0;  /* Loop counter */

    if(argc >= 1)
    {
      printf("Usage: %s <options>\n", argv[0]);
      printf("  Where <options> may contain any of the following:\n");
    }

    printf("\n%-16s %s %s\n", "Name", "aka", "Description");
    printf("%-16s %s %s\n", "----", "---", "-----------");

    for(i = 0; i < schema->ArgCount; i++)
      PrintArgOverview(&schema->Args[i]);

    printf("\n");
  }
  else
  {
    char logmsg[64];
    sprintf(logmsg, "PrintArgSchema: Invalid Schema\n");
    WriteMessage(LogLevelError, logmsg);
    retval = NVN_EINVARGS;
  }

  return retval;
}

int ValidateArgs(int argc, char* argv[], CLSchema* schema, int* valid)
{
  int retval = NVN_NOERR;

  if(schema && valid)
  {
    int i = 0;              /* Loop counter */
    const ArgDef* def = 0;  /* Temporary ArgDef pointer */
    char logmsg[64];

    *valid = 1;
    for(i = 1; i < argc; i++)
    {
      if(strlen(argv[i]) > 1 && (argv[i])[0] == '-')
      {
        def = GetArgDef(&((argv[i])[1]), schema);
        if(! def)
        {
          sprintf(logmsg, "Unrecognized argument: \"%s\"", argv[i]);
          WriteMessage(LogLevelError, logmsg);
          *valid = 0;
        }
        else if(def && def->RequiresValue && i+1 >= argc)
        {
          sprintf(logmsg, "A value is required for \"%s\", but it was not supplied", argv[i]);
          WriteMessage(LogLevelError, logmsg);
          *valid = 0;
        }
        else if(def && def->RequiresValue)
        {
          i++;
        }
      }
      else
      {
        sprintf(logmsg, "Format error in arguments: \"%s\"", argv[i]);
        WriteMessage(LogLevelError, logmsg);
        *valid = 0;
      }
    }
  }
  else
  {
    retval = NVN_EINVARGS;
  }

  return retval;
}


/******************************************************************************
* Implementations for local functions:
******************************************************************************/

const ArgDef* GetArgDef(const char* s, CLSchema* schema)
{
  ArgDef* retval = 0;
  int i = 0;

  if(s && schema)
  {
    for(i = 0; !retval && i < schema->ArgCount; i++)
    {
      if((strlen(s) == 1 && s[0] == schema->Args[i].Abbrev) || 
        0 == strcmp(schema->Args[i].Name, s))
        retval = &schema->Args[i];
    }
  }

  return retval;
}

void PrintArgOverview(ArgDef* arg)
{
  int descWidth = 80 - 18;
  int descLen = 0;
  char descLine[80];
  int start = 0;
  int stop = 0;
  int line = 0;

  if(arg)
  {
    descLen = strlen(arg->Description);
    while(start < descLen)
    {
      while(arg->Description[start] == ' ')
        start++;

      stop = start + descWidth;
      if(stop >= descLen)
        stop = descLen - 1;
      else			
        while(stop > start && arg->Description[stop] != ' ')
          stop--;

      if(stop > start)
      {
        if(line == 0)
        {
          if(arg->Abbrev > 0)
            printf("-%-16s %c  ", arg->Name, arg->Abbrev);
          else
            printf("-%-16s    ", arg->Name);
        }	    
        else
        {
          printf("%20s", "");
        }

        memset(descLine, 0, 80);
        strncpy(descLine, &arg->Description[start], stop - start + 1);
        printf("%s", descLine);
      }

      line++;
      start = stop + 1;
    }
  }
}
